{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# DP Tensor Experiment - v8\n",
    "\n",
    "### Purpose:\n",
    "\n",
    "\n",
    "### Conclusions:\n",
    "\n",
    "simple change - time to vectorize!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "from collections import Counter\n",
    "import numpy as np\n",
    "\n",
    "class PrivateNumber():\n",
    "    \n",
    "    def __init__(self, value, max_val, min_val):\n",
    "        self.value = value\n",
    "        self.max_val = max_val\n",
    "        self.min_val = min_val\n",
    "        \n",
    "    def __add__(self, other):\n",
    "        \n",
    "        # add to a private number\n",
    "        \n",
    "        if(isinstance(other, PrivateNumber)):\n",
    "\n",
    "            entities = self.entities.union(other.entities)\n",
    "            \n",
    "            new_val = self.value + other.value\n",
    "\n",
    "            entities = set(self.max_val.keys()).union(set(other.max_val.keys()))\n",
    "\n",
    "            new_max_val = {}\n",
    "            new_min_val = {}\n",
    "            for entity in entities:\n",
    "                \n",
    "                self_max = self.max_val[entity] if entity in self.max_val else 0\n",
    "                other_max = other.max_val[entity] if entity in other.max_val else 0\n",
    "                \n",
    "                self_min = self.min_val[entity] if entity in self.min_val else 0\n",
    "                other_min = other.min_val[entity] if entity in other.min_val else 0\n",
    "                \n",
    "                new_max_val[entity] = self_max + other_max\n",
    "                new_min_val[entity] = self_min + other_min\n",
    "\n",
    "        else:\n",
    "\n",
    "            entities = self.entities\n",
    "\n",
    "            # add to a public number\n",
    "            new_val = self.value + other\n",
    "            \n",
    "            new_max_val = {}\n",
    "            new_min_val = {}\n",
    "            for entity in entities:\n",
    "                new_max_val[entity] = self.max_val[entity] + other\n",
    "                new_min_val[entity] = self.min_val[entity] + other\n",
    "\n",
    "        return PrivateNumber(new_val,\n",
    "                             new_max_val,\n",
    "                             new_min_val)\n",
    "    \n",
    "    def __mul__(self, other):\n",
    "        \n",
    "        if(isinstance(other, PrivateNumber)):\n",
    "        \n",
    "            new_self_max_val = {}\n",
    "            new_self_min_val = {}\n",
    "            for entity in self.entities:\n",
    "                \n",
    "                # the biggest positive number this entity could contribute is when\n",
    "                # it is multiplied by the largest value of the same sign from other\n",
    "                new_self_max_val[entity] = max(self.min_val[entity] * other.xmin, \n",
    "                                               self.max_val[entity] * other.xmax)\n",
    "                \n",
    "                # the smallest negative number this entity could contribute is when\n",
    "                # it is multiplied by the largest value of the opposite sign from other\n",
    "                new_self_min_val[entity] = min(self.min_val[entity] * other.xmax,\n",
    "                                               self.max_val[entity] * other.xmin)\n",
    "                \n",
    "            new_other_max_val = {}\n",
    "            new_other_min_val = {}\n",
    "            for entity in other.entities:\n",
    "                \n",
    "                # the biggest positive number this entity could contribute is when\n",
    "                # it is multiplied by the largest value of the same sign from other\n",
    "                new_other_max_val[entity] = max(other.min_val[entity] * self.xmin, \n",
    "                                                other.max_val[entity] * self.xmax)\n",
    "                \n",
    "                # the smallest negative number this entity could contribute is when\n",
    "                # it is multiplied by the largest value of the opposite sign from other\n",
    "                new_other_min_val[entity] = min(other.min_val[entity] * self.xmax,\n",
    "                                                other.max_val[entity] * self.xmin)\n",
    "                \n",
    "            new_max_val = {}\n",
    "            new_min_val = {}\n",
    "            \n",
    "            for entity in self.entities:\n",
    "                \n",
    "                left = new_self_max_val[entity] if entity in new_self_max_val else -(2**32)\n",
    "                right = new_other_max_val[entity] if entity in new_other_max_val else -(2**32)\n",
    "                \n",
    "                new_max_val[entity] = max(left, right)\n",
    "                \n",
    "                left = new_self_min_val[entity] if entity in new_self_min_val else 2**32\n",
    "                right = new_other_min_val[entity] if entity in new_other_min_val else 2**32\n",
    "                \n",
    "                new_min_val[entity] = min(left, right)\n",
    "\n",
    "            return PrivateNumber(self.value * other.value,\n",
    "                                    new_max_val,\n",
    "                                    new_min_val)\n",
    "            \n",
    "    \n",
    "        new_max_val = {}\n",
    "        for entity in self.entities:\n",
    "            new_max_val[entity] = self.max_val[entity] * other\n",
    "\n",
    "        new_min_val = {}\n",
    "        for entity in self.entities:\n",
    "            new_min_val[entity] = self.min_val[entity] * other\n",
    "        \n",
    "        if(other > 0):\n",
    "            return PrivateNumber(self.value * other,\n",
    "                                    new_max_val,\n",
    "                                    new_min_val)\n",
    "        else:\n",
    "            return PrivateNumber(self.value * other,\n",
    "                                    new_min_val,                                 \n",
    "                                    new_max_val)\n",
    "    \n",
    "    def __neg__(self):\n",
    "        return self * -1\n",
    "    \n",
    "    def __sub__(self, other):\n",
    "        return self + (-other)\n",
    "\n",
    "    def __gt__(self, other):\n",
    "\n",
    "        if(isinstance(other, PrivateNumber)):\n",
    "        \n",
    "            new_self_max_val = {}\n",
    "            new_self_min_val = {}\n",
    "            for entity in self.entities:\n",
    "                \n",
    "                if not (self.min_val[entity] > other.xmax or self.max_val[entity] < other.xmin):\n",
    "                    new_self_max_val[entity] = 1\n",
    "                else:\n",
    "                    new_self_max_val[entity] = 0\n",
    "                \n",
    "                new_self_min_val[entity] = 0\n",
    "                \n",
    "            new_other_max_val = {}\n",
    "            new_other_min_val = {}\n",
    "            for entity in other.entities:\n",
    "                \n",
    "                if not (other.min_val[entity] > self.xmax or other.max_val[entity] < self.xmin):\n",
    "                    new_other_max_val[entity] = 1\n",
    "                else:\n",
    "                    new_other_max_val[entity] = 0\n",
    "                    \n",
    "                new_other_min_val[entity] = 0\n",
    "                \n",
    "            new_max_val = {}\n",
    "            new_min_val = {}\n",
    "            \n",
    "            entities = self.entities.union(other.entities)\n",
    "                \n",
    "            for entity in entities:\n",
    "                \n",
    "                new_self_max = new_self_max_val[entity] if entity in new_self_max_val else -999999999\n",
    "                new_other_max = new_other_max_val[entity] if entity in new_other_max_val else -999999999\n",
    "                \n",
    "                new_self_min = new_self_min_val[entity] if entity in new_self_min_val else 99999999\n",
    "                new_other_min = new_other_min_val[entity] if entity in new_other_min_val else 99999999\n",
    "                \n",
    "                new_max_val[entity] = max(new_self_max, new_other_max)\n",
    "                new_min_val[entity] = min(new_self_min, new_other_min)\n",
    "\n",
    "            result = int(self.value > other.value)\n",
    "        else:\n",
    "\n",
    "            entities = self.entities.union(other.entities)\n",
    "\n",
    "            new_max_val = {}\n",
    "            new_min_val = {}\n",
    "            for entity in entities:\n",
    "\n",
    "                new_min_val[entity] = 0\n",
    "\n",
    "                if(other <= self.max_val[entity] and other >= self.min_val[entity]):    \n",
    "                    new_max_val[entity] = 1\n",
    "                else:\n",
    "                    new_max_val[entity] = 0\n",
    "\n",
    "            result = int(self.value > other)\n",
    "            \n",
    "        return PrivateNumber(result,\n",
    "                                new_max_val,\n",
    "                                new_min_val)\n",
    "    \n",
    "    def __lt__(self, other):\n",
    "        \n",
    "        if(isinstance(other, PrivateNumber)):\n",
    "        \n",
    "            entities = self.entities.union(other.entities)\n",
    "        \n",
    "            new_self_max_val = {}\n",
    "            new_self_min_val = {}\n",
    "            \n",
    "            for entity in self.entities:\n",
    "                \n",
    "                if not (self.min_val[entity] > other.xmax or self.max_val[entity] < other.xmin):\n",
    "                    new_self_max_val[entity] = 1\n",
    "                else:\n",
    "                    new_self_max_val[entity] = 0\n",
    "                \n",
    "                new_self_min_val[entity] = 0\n",
    "                \n",
    "            new_other_max_val = {}\n",
    "            new_other_min_val = {}\n",
    "            \n",
    "            for entity in other.entities:\n",
    "                \n",
    "                if not (other.min_val[entity] > self.xmax or other.max_val[entity] < self.xmin):\n",
    "                    new_other_max_val[entity] = 1\n",
    "                else:\n",
    "                    new_other_max_val[entity] = 0\n",
    "                    \n",
    "                new_other_min_val[entity] = 0\n",
    "                \n",
    "            new_max_val = {}\n",
    "            new_min_val = {}\n",
    "            \n",
    "            entities = self.entities.union(other.entities)\n",
    "                \n",
    "            for entity in entities:\n",
    "                \n",
    "                new_self_max = new_self_max_val[entity] if entity in new_self_max_val else -999999999\n",
    "                new_other_max = new_other_max_val[entity] if entity in new_other_max_val else -999999999\n",
    "                \n",
    "                new_self_min = new_self_min_val[entity] if entity in new_self_min_val else 99999999\n",
    "                new_other_min = new_other_min_val[entity] if entity in new_other_min_val else 99999999\n",
    "                \n",
    "                new_max_val[entity] = max(new_self_max, new_other_max)\n",
    "                new_min_val[entity] = min(new_self_min, new_other_min)\n",
    "\n",
    "            result = int(self.value < other.value)\n",
    "        \n",
    "        else:\n",
    "\n",
    "            entities = self.entities\n",
    "\n",
    "            new_max_val = {}\n",
    "            new_min_val = {}\n",
    "            for entity in entities:\n",
    "\n",
    "                new_min_val[entity] = 0\n",
    "\n",
    "                if(other <= self.max_val[entity] and other >= self.min_val[entity]):    \n",
    "                    new_max_val[entity] = 1\n",
    "                else:\n",
    "                    new_max_val[entity] = 0\n",
    "\n",
    "            result = int(self.value < other)\n",
    "        \n",
    "        return PrivateNumber(result,\n",
    "                             new_max_val,\n",
    "                             new_min_val)\n",
    "    \n",
    "    def max(self, other):\n",
    "        \n",
    "        if(isinstance(other, PrivateNumber)):\n",
    "            raise Exception(\"Not implemented yet\")\n",
    "        \n",
    "        entities = self.entities\n",
    "        \n",
    "        new_min_val = {}\n",
    "        for entity in entities:\n",
    "            new_min_val[entity] = max(self.min_val[entity], other)\n",
    "            \n",
    "        return PrivateNumber(max(self.value, other),\n",
    "                                self.max_val,\n",
    "                                new_min_val)\n",
    "    \n",
    "    def min(self, other):\n",
    "        \n",
    "        if(isinstance(other, PrivateNumber)):\n",
    "            raise Exception(\"Not implemented yet\")\n",
    "        \n",
    "        entities = self.entities\n",
    "        \n",
    "        new_max_val = {}\n",
    "        for entity in entities:\n",
    "            new_max_val[entity] = min(self.max_val[entity], other)\n",
    "                \n",
    "        return PrivateNumber(min(self.value, other),\n",
    "                                new_max_val,\n",
    "                                self.min_val)\n",
    "    \n",
    "    def __repr__(self):\n",
    "        return str(self.value) + \" \" + str(self.max_val) + \" \" + str(self.min_val)\n",
    "    \n",
    "    def hard_sigmoid(self):\n",
    "        return self.min(1).max(0)\n",
    "    \n",
    "    def hard_sigmoid_deriv(self):\n",
    "        return ((self < 1) * (self > 0)) + (self < 0) * 0.01 - (self > 1) * 0.01\n",
    "    \n",
    "    @property\n",
    "    def xmin(self):\n",
    "        items = list(self.min_val.items())\n",
    "        out = items[0][1]\n",
    "        for k,v in items[1:]:\n",
    "            out += v\n",
    "        return out\n",
    "    \n",
    "    @property\n",
    "    def xmax(self):\n",
    "        items = list(self.max_val.items())\n",
    "        out = items[0][1]\n",
    "        for k,v in items[1:]:\n",
    "            out += v\n",
    "        return out\n",
    "    \n",
    "    @property\n",
    "    def entities(self):\n",
    "        return set(self.max_val.keys())\n",
    "    \n",
    "    @property\n",
    "    def max_sensitivity(self):\n",
    "        sens = Counter()\n",
    "        for entity, value in self.max_val.items():\n",
    "            sens[entity] = value - self.min_val[entity]\n",
    "        return sens.most_common()[0][1]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "x = PrivateNumber(0.5,{\"bob\":4, \"amos\":3},{\"bob\":3, \"amos\":2})\n",
    "x2 = PrivateNumber(0.5,{\"bob\":4, \"amos\":3},{\"bob\":3, \"amos\":2})\n",
    "y = PrivateNumber(1,{\"bob\":1},{\"bob\":-1})\n",
    "z = PrivateNumber(-0.5,{\"sue\":2},{\"sue\":-1})"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0.5 {'bob': 4, 'amos': 3} {'bob': 3, 'amos': 2}"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "x"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": 78,
   "metadata": {},
   "outputs": [],
   "source": [
    "a = (x * x2)# > y"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 79,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0.25 {'bob': 28, 'amos': 21} {'bob': 20, 'amos': 14}"
      ]
     },
     "execution_count": 79,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "a"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 69,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0"
      ]
     },
     "execution_count": 69,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "a.max_sensitivity"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "\n"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.1"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
